﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using Kugar.Core.Communications.Transfer;
using Kugar.Core.ExtMethod;
using Kugar.Core.BaseStruct;

namespace Kugar.Core.Communications.DualCommunication
{
    public class DualTransfer
    {
        private Random rnd = new Random();

        public DualTransfer(ITransfer transfer)
        {
            Transfer = transfer;

            transfer.AfterReceiveData += Transfer_AfterReceiveData;

            this.CmdIDMaxValue = 0;
            this.CmdIDMinValue = int.MinValue;//服务器端生成的ID,全部为负数
        }

        protected readonly Dictionary<int, DualSendStructBase> cacheCmd = new Dictionary<int, DualSendStructBase>();

        public virtual void SendAsyn(DualSendStructBase sendData)
        {
            sendData.CmdID = GetCmdID();

            try
            {
                lock (cacheCmd)
                {
                    cacheCmd.Add(sendData.CmdID, sendData);
                }

                Transfer.SendAsync(sendData.Data, 0, sendData.Data.Length,null,null);
            }
            catch (Exception)
            {
                lock (cacheCmd)
                {
                    cacheCmd.Remove(sendData.CmdID);
                }
            }


        }

        /// <summary>
        ///     当接收到非回复指令的数据包时,将引发该事件,,,该事件用于处理远程端主动提交信息时,的处理方法
        /// </summary>
        public event EventHandler<DualReceiveEventArgs> DataRecevie;

        public DataCheckFunction RecevieDataChecker { set; get; }

        public ITransfer Transfer { private set; get; }

        public int CmdIDMaxValue { set; get; }

        public int CmdIDMinValue { set; get; }

        protected int GetCmdID()
        {

            var cmdID = 0;

            lock (cacheCmd)
            {
                //循环获取
                while (true)
                {
                    cmdID = rnd.Next(CmdIDMinValue, CmdIDMaxValue);

                    if (cmdID != 0 && !cacheCmd.ContainsKey(cmdID))
                    {
                        break;
                    }
                }
            }

            return cmdID;
        }

        protected virtual void Transfer_AfterReceiveData(object sender, DataReceiveEventArgs e)
        {
            var data = e.ReceiveData;

            if (data.Count <= 0)
            {
                return;
            }



            if (RecevieDataChecker != null && !RecevieDataChecker.Invoke(data))
            {
                return;
            }

            var tempStruct = DataPacketStruct.ReadFrom(data.Array,data.Offset,data.Count);

            if (tempStruct == null)
            {
                return;
            }

            if (cacheCmd.ContainsKey(tempStruct.CmdID))
            {
                var ea = new RaiseCmdCallbackObj();
                ea.SendedStruct = cacheCmd[tempStruct.CmdID];
                ea.DataStruct = tempStruct;
                ea.Error = e.Error;

                ThreadPool.QueueUserWorkItem(RaiseSenderCmdRevCallback, ea);

            }
            else
            {
                ThreadPool.QueueUserWorkItem(OnDataRecevie, tempStruct);
            }

        }

        protected virtual void OnDataRecevie(object state)
        {
            if (state == null || !(state is DataPacketStruct))
            {
                return;
            }

            if (DataRecevie != null)
            {
                DataRecevie(this, new DualReceiveEventArgs((DataPacketStruct)state));
            }
        }

        protected virtual void RaiseSenderCmdRevCallback(object state)
        {
            if (state == null || !(state is RaiseCmdCallbackObj))
            {
                return;
            }

            var par = (RaiseCmdCallbackObj)state;

            lock (cacheCmd)
            {
                cacheCmd.Remove(par.SendedStruct.CmdID);
            }

            try
            {
                par.SendedStruct.OnAfterReceiveData(this,new ArraySegment<byte>( par.DataStruct.Data), par.Error);
            }
            catch (Exception)
            {

            }

        }

        protected class RaiseCmdCallbackObj
        {
            public DataPacketStruct DataStruct;
            public DualSendStructBase SendedStruct;
            public Exception Error;

            public bool HasError { get { return Error != null; } }
        }

        public delegate bool DataCheckFunction(ArraySegment<byte> revData);
    }


    public class DualTransferWithQueue : DualTransfer
    {
        private readonly SendQueue<DualSendStructBase> sq = new SendQueue<DualSendStructBase>();
        //private Timer timerClearSendedStruct = null;
        private Timer timerSendStruct = null;
        private DualSendStructBase tempSendedStruct = null;


        public DualTransferWithQueue(ITransfer transfer)
            : base(transfer)
        {
            //timerClearSendedStruct = new Timer(OnClearSendedStruct, null, Timeout.Infinite, Timeout.Infinite);
            timerSendStruct = new Timer(OnSendCheck, null, 500, 0);
        }

        public int SendDelay { set; get; }

        public override void SendAsyn(DualSendStructBase sendData)
        {
            sq.EnQueue(sendData);
        }

        protected override void RaiseSenderCmdRevCallback(object state)
        {
            base.RaiseSenderCmdRevCallback(state);

            var par = (RaiseCmdCallbackObj)state;

            CallbackTimer.Default.Call(this.SendDelay,OnClearSendedStruct, par.SendedStruct);

            //timerClearSendedStruct = new Timer(, SendDelay, 0);
        }


        private void OnClearSendedStruct(object state)
        {
            if (state != null)
            {
                lock (state)
                {
                    if (state != null)
                    {
                        if (state == tempSendedStruct)
                        {
                            Interlocked.Exchange(ref tempSendedStruct, null);
                        }
                    }
                }
            }
        }

        private void OnSendCheck(object state)
        {
            if (tempSendedStruct == null)
            {
                //timerClearSendedStruct.Change(Timeout.Infinite, Timeout.Infinite);

                var item = sq.DeQueue();

                if (item != null)
                {
                    base.SendAsyn(item);

                    Interlocked.Exchange(ref tempSendedStruct, item);
                }
            }
            else
            {
                lock (tempSendedStruct)
                {
                    if (tempSendedStruct != null) //再次检查是否为空，可以防止在if之后，锁定之前dataSended变量的值被改变
                    {
                        if (tempSendedStruct.SendTime > tempSendedStruct.SendTime.AddMilliseconds(tempSendedStruct.SendTimeOutTime))
                        {
                            if (tempSendedStruct.ReSendCount > 0)
                            {
                                tempSendedStruct.SendTime = DateTime.Now;
                                tempSendedStruct.SendTimeOutTime--;

                                sq.EnQueue(tempSendedStruct);
                            }

                            Interlocked.Exchange(ref tempSendedStruct, null);
                        }
                    }
                }
            }

            timerSendStruct.Change(500, 0);
        }


    }



    public class DualSendStructBase : DataStructToSendBase
    {
        public int CmdID { internal set; get; }

        public override bool IsCanJoinCmd(DataStructToSendBase other)
        {
            return false;
        }
    }

    public class DualReceiveEventArgs : EventArgs
    {
        public DualReceiveEventArgs()
        {
            ReceiveDateTime = DateTime.Now;
        }

        public DualReceiveEventArgs(DataPacketStruct dataReceive)
            : this()
        {
            DataReceive = dataReceive;
        }

        public DateTime ReceiveDateTime { get; private set; }

        public DataPacketStruct DataReceive;
    }

    public class DataPacketStruct
    {

        public int CmdID;
        public byte[] Data;

        public byte[] ToBytes()
        {
            var dataLength = 4;

            if (Data!=null)
            {
                dataLength += Data.Length;
            }

            var d = new byte[dataLength];

            Array.Copy(BitConverter.GetBytes(CmdID),0,d,0,4);

            if (Data!=null && Data.Length>0)
            {
                Array.Copy(Data,0,d,4,Data.Length);
            }
            

            return d;
        }

        public static DataPacketStruct ReadFrom(byte[] data,int offset,int count)
        {
            if (data == null || count < 4 || data.IsInEnableRange(offset,count))
            {
                throw new ArgumentOutOfRangeException("data");
            }

            var temp = new DataPacketStruct();

            temp.CmdID = BitConverter.ToInt32(data, offset);

            var d = new byte[count - 4];

            Array.Copy(data, offset + 4, d, 0, count - 4);

            return temp;
        }
    }



}
